19장 데이터베이스 연결하기 (입문판)
출처: 클로드 코드 완벽 가이드 (코드팩토리 최지호)
이 장에서 딱 3가지만 기억하세요. 첫째, 앱이 데이터를 "기억"하려면 데이터베이스(DB)가 필요하다. 둘째, 클로드 코드가 DB를 다루게 하려면 도구 두 개(Supabase MCP, Context7 MCP)를 끼워준다. 셋째, DB로 갈아탈 때는 "계획 먼저, 구현 나중" 2단계로 안전하게 한다.
들어가기 전에
여기서 처음 나오는 말 몇 개를 한 줄씩 풀어둘게요. 모르는 단어가 나오면 여기로 돌아오면 됩니다.
데이터베이스(DB)는 데이터를 영구히 저장하는 창고예요. 일상 비유로는 "메모장" 같은 거예요. 적어두면 나중에 다시 볼 수 있죠.
Supabase는 그 창고를 인터넷에 빌려주는 서비스예요. 무료로 시작할 수 있는 "임대 창고"라고 보면 됩니다.
MCP는 클로드 코드가 바깥 도구와 대화하게 해주는 "통역사"예요. (MCP 자체는 16장에서 다뤘어요. 여기선 통역사라고만 알아도 됩니다.)
테이블은 DB 안의 "표 한 장"이에요. 엑셀 시트 한 장과 비슷해요.
마이그레이션은 데이터를 옛 집에서 새 집으로 옮기는 "이사"예요.
이 단어들은 본문에서 다시 천천히 풀어줄 테니, 지금 외우지 않아도 됩니다.
학습 목표
이 장을 끝내면 다음을 할 수 있어요.
DB가 왜 필요한지 메모장 비유로 설명한다. 가짜 데이터로 먼저 만들고 나중에 DB로 바꾸는 이유를 설명한다. Supabase MCP와 Context7 MCP를 프로젝트에 등록하는 절차를 따라 한다. 두 MCP의 역할 차이를 구분한다. "계획 먼저, 구현 나중" 2단계 마이그레이션을 따라 한다.
19-1 데이터베이스가 왜 필요할까
이런 적 있죠?
메모 앱을 하나 만들었다고 해봐요. 글을 적고 저장 버튼을 눌렀어요. 그런데 새로고침을 했더니 글이 싹 사라졌어요.
분명 저장을 눌렀는데 왜 없어졌을까요? 그건 글을 진짜로 어딘가에 "적어둔" 게 아니라, 화면 위에서만 잠깐 들고 있었기 때문이에요.
이걸 막아주는 게 바로 데이터베이스예요.
그게 데이터베이스예요
데이터베이스(DB)는 데이터를 영구히 보관하는 창고예요. 한 번 적으면, 새로고침을 해도, 다른 폰으로 켜도 그대로 남아 있어요.
DB가 없는 앱은 기억력이 없는 사람과 같아요. 방금 들은 말도 돌아서면 잊어버리죠.
DB가 있는 앱은 메모장을 가진 사람과 같아요. 중요한 건 적어두니까 언제든 다시 꺼내볼 수 있어요.
| 비유 | 클로드 코드에서 | 주의 |
|---|---|---|
| 기억력 없는 사람 | DB 없는 앱 (새로고침하면 데이터 증발) | MVP 확인용으론 충분 |
| 메모장 가진 사람 | DB 연결한 앱 (저장하면 계속 남음) | 진짜 서비스엔 필수 |
| 메모장 자체 | Supabase 같은 DB 서버 | 무료 티어로 연습 가능 |
웹 앱의 기본 골격
웹 앱은 보통 세 칸으로 나뉘어요. 화면, 서버, 창고예요.
프론트엔드(화면) → 백엔드(서버) → DB(창고)
사용자가 봄 중간에서 처리 데이터 보관
화면(프론트엔드)에서 "이거 저장해줘"라고 보내면, 서버(백엔드)가 받아서, 창고(DB)에 차곡차곡 넣어요.
꺼낼 때도 똑같은 길을 거꾸로 거쳐요. 이 3칸 구조가 거의 모든 웹 앱의 기본 골격이에요.
가짜 데이터 vs 진짜 DB
지금까지(18장까지) 만든 앱은 "가짜 데이터"로 돌아갔어요. 가짜 데이터는 코드 안에 직접 적어둔 임시 데이터예요. 일상 비유로는 "연습용 마네킹" 같은 거예요. 모양만 진짜처럼 보이죠.
진짜 DB는 바깥 창고에 데이터를 두는 거예요.
| 비교 | 가짜 데이터 (18장까지) | 진짜 DB (19장부터) |
|---|---|---|
| 데이터 위치 | 코드 안 (변수) | 바깥 DB 서버 (Supabase) |
| 새로고침하면 | 초기화됨 | 그대로 유지됨 |
| 사용자별 저장 | 안 됨 | 됨 |
| 다른 기기에서 보기 | 안 됨 | 됨 |
| 용도 | MVP 모양 확인 | 진짜 서비스 운영 |
왜 가짜 데이터를 먼저 쓸까
"처음부터 진짜 DB 붙이면 되잖아?"라고 생각할 수 있어요. 그런데 저자는 가짜 데이터부터 쓰라고 추천해요.
잘못된 예) 처음부터 DB까지 다 붙이기
화면 + 서버 + DB를 한꺼번에 만든다
→ 셋이 뒤엉켜서 복잡함 폭발
→ 버그가 나면 "DB 문제? API 문제? 화면 문제?" 헷갈림
올바른 예) 가짜 데이터로 먼저 만들기 (저자 추천)
화면 먼저 완성 → 잘 도는지 확인 → 그다음 DB 연결
→ 칸이 하나씩 나뉘어서 문제 찾기 쉬움
저자의 말로는 "작업 레이어가 명확히 나뉘어 더 좋은 결과가 나온다"고 해요. 한 번에 다 하려다 엉키느니, 한 칸씩 확실히 하는 거죠.
이럴 때 이렇게: 화면이 멀쩡히 잘 도는 걸 눈으로 확인했다면, 그때가 바로 진짜 DB로 갈아탈 좋은 시점이에요.
잠깐 점검
여기까지 이해했는지 스스로 물어보세요.
화면 → 서버 → DB 3칸 구조를 설명할 수 있나요? 가짜 데이터를 먼저 쓰는 이유를 말할 수 있나요? DB 없는 앱의 한계를 3가지 댈 수 있나요?
19-2 필수 도구(MCP) 두 개 끼우기
이런 적 있죠?
클로드 코드한테 "DB에 테이블 만들어줘"라고 했어요. 그런데 클로드 코드는 코드만 적어줄 뿐, 실제로 DB를 건드리진 못해요.
왜냐하면 클로드 코드는 우리 컴퓨터 안에 있고, DB는 인터넷 저 멀리 Supabase에 있거든요. 둘 사이에 "다리"가 없는 거예요.
이 다리를 놓아주는 게 MCP예요.
그게 MCP예요
MCP는 클로드 코드와 바깥 서비스 사이의 통역사예요. (MCP가 무엇인지는 16장에서 다뤘어요. 여기선 통역사로만 봐도 충분해요.)
이 장에서는 통역사 두 명을 고용할 거예요. 한 명은 DB 전문, 한 명은 문서 전문이에요.
Supabase MCP (DB 전문 통역사)
클로드 코드 ↔ Supabase MCP ↔ Supabase DB
"테이블 만들어줘" → MCP가 SQL로 번역 → DB에 실행
Context7 MCP (문서 전문 통역사)
클로드 코드 ↔ Context7 MCP ↔ 최신 공식 문서
"최신 사용법 알려줘" → 공식 문서 검색 → 최신 정보 전달
| 비유 | 클로드 코드에서 | 주의 |
|---|---|---|
| DB 전문 통역사 | Supabase MCP (테이블 만들고 쿼리 실행) | 인증 토큰 필요 |
| 문서 전문 통역사 | Context7 MCP (최신 문서 찾아줌) | 인증 없이 등록 간편 |
| 통역사 두 명 | 둘 다 켜야 "최신 정보 + 직접 실행" 완성 | 하나만 켜면 반쪽 |
왜 두 개 다 필요할까
한 명만 고용하면 어떻게 될까요?
Supabase MCP만 있으면: DB는 다루지만 최신 사용법을 모를 수 있어요. Context7 MCP만 있으면: 문서는 알지만 DB를 직접 못 건드려요. 둘 다 있으면: 최신 정보로 알아내서 + 바로 실행까지 해요.
그래서 이 장에선 둘 다 끼웁니다.
왜 Supabase를 골랐을까
DB 서비스는 여럿이지만 이 책은 Supabase를 골랐어요. 이유는 두 가지예요.
1. 무료 티어가 있어서 돈 안 들이고 연습할 수 있다
2. MCP가 잘 만들어져 있어서 클로드 코드와 잘 맞는다
(여기 나오는 무료 티어 범위는 원서/노트 기준 예시이며, 최신 값은 Supabase 공식 문서에서 확인하세요.)
한 걸음 더 ▸ Supabase와 Firebase는 뭐가 다를까
DB 서비스로 Firebase라는 것도 많이 들어봤을 거예요. 둘 다 백엔드를 빌려주지만 성격이 좀 달라요. 지금 깊이 알 필요는 없고, 표만 가볍게 훑어도 돼요.
| 비교 | Supabase | Firebase |
|---|---|---|
| DB 종류 | PostgreSQL (표 기반, 관계형) | Firestore (문서 기반) |
| 오픈소스 | 맞음 | 아님 |
| MCP 지원 | 공식 MCP 있음 | 비공식 |
| SQL | 그대로 씀 | 별도 쿼리 방식 |
이 책이 Supabase를 고른 건, 표 기반이라 SQL을 그대로 쓰고 공식 MCP가 있어 클로드 코드와 잘 맞기 때문이에요.
통역사 1: Supabase MCP 등록하기
Supabase MCP는 설정 파일을 직접 적어서 등록해요.
그 파일 이름이 .mcp.json이에요.
프로젝트 폴더 맨 위(루트)에 두는 MCP 설정표예요.
기본 사용법(안전한 길)부터 차례대로 가볼게요.
1단계: 프로젝트 루트에 .mcp.json 파일을 만든다
2단계: 그 안에 supabase 서버 정보를 적는다
3단계: PAT(인증 토큰)를 발급받는다
4단계: Project Ref(프로젝트 번호)를 확인한다
5단계: 적어둔 빈칸을 진짜 값으로 바꿔 넣는다
3단계 PAT부터 봐요. PAT(Personal Access Token)는 Supabase가 주는 출입증이에요. 일상 비유로는 "신분증" 같은 거예요. MCP가 DB 문 앞에서 이걸 보여줘요.
PAT 발급: supabase.com/dashboard/account/tokens 에 접속해요. Generate New Token을 눌러요. 이름을 적어요 (예: "Claude Code MCP"). 나온 토큰을 복사해요.
주의: PAT는 딱 한 번만 보여줘요. 그 순간 복사해서 따로 저장하지 않으면 다시 못 봐요.
4단계 Project Ref는 프로젝트의 고유 번호예요. Supabase 대시보드에서 Settings > General > Project ID를 복사하면 돼요.
이제 .mcp.json은 이렇게 생겼어요.
{
"mcpServers": {
"supabase": {
"command": "npx",
"args": [
"-y",
"@supabase/mcp-server-supabase@latest",
"--read-only",
"--project-ref=여기에_Project_Ref"
],
"env": {
"SUPABASE_ACCESS_TOKEN": "여기에_PAT"
}
}
}
}
각 줄을 한 마디로 풀면 이래요.
command는 "무엇으로 실행하나" → npx (Node 패키지 실행 도구)예요. args는 "어떤 옵션으로" → 패키지 이름과 옵션들이에요. env는 "어떤 비밀값으로" → 인증 토큰을 넣는 칸이에요.
(패키지 이름이나 옵션은 원서/노트 기준 예시예요. 최신 패키지명과 옵션은 Supabase 공식 문서에서 확인하세요.)
한 걸음 더 ▸ --read-only가 뭐예요?
위 args에 --read-only가 보이죠.
이건 "읽기만 하고 함부로 못 바꾸게" 막는 안전 빗장이에요.
처음 연결을 확인할 땐 이렇게 읽기 전용으로 두면 사고를 줄여요. 나중에 진짜로 테이블을 만들어야 하면 이 옵션을 빼야 동작해요. (옵션 동작은 원서/노트 기준 예시이며, 정확한 사용법은 공식 문서에서 확인하세요.)
지금은 "이런 안전 옵션이 있구나" 정도만 알아도 됩니다.
통역사 2: Context7 MCP 등록하기
Context7 MCP는 더 쉬워요. 파일을 손으로 안 적고, 명령어 한 줄로 끝나요.
Context7 MCP는 Upstash라는 곳에서 제공하는 통역사예요. 최신 공식 문서를 실시간으로 찾아다 줘요.
# 프로젝트 수준으로 등록 (-s project)
claude mcp add context7 -s project -- npx -y @upstash/context7-mcp
이 한 줄을 치면 .mcp.json에 context7가 자동으로 추가돼요.
그러면 파일은 이렇게 둘 다 들어 있게 돼요.
{
"mcpServers": {
"supabase": { ... },
"context7": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"],
"env": {}
}
}
}
Context7는 인증이 필요 없어서 env 칸이 비어 있어요. (패키지명은 원서/노트 기준 예시이며, 최신 정보는 Context7 공식 저장소에서 확인하세요.)
잘 끼워졌는지 확인하기
/mcp 명령어를 치면 등록된 MCP 목록이 보여요.
/mcp 실행 결과:
context7 — 활성화 상태
supabase — 활성화 상태
둘 다 활성화로 떠야 정상이에요. 하나라도 빠지거나 꺼져 있으면, 위 등록 단계를 다시 점검하세요.
두 통역사 한눈에 비교
| 비교 | Supabase MCP | Context7 MCP |
|---|---|---|
| 등록 방법 | .mcp.json 직접 작성 | 명령어 한 줄 |
| 인증 필요 | 필요 (PAT + Project Ref) | 안 필요 |
| 역할 | DB 직접 조작 (테이블·쿼리) | 최신 문서 찾아주기 |
| 제공처 | Supabase 공식 | Upstash |
보안 주의: 토큰은 절대 Git에 올리지 마세요
여기 꼭 짚고 갈 함정이 있어요.
.mcp.json 안에 들어간 PAT는 비밀번호 같은 거예요.
잘못된 예)
.mcp.json에 PAT를 그대로 둔 채 Git에 커밋한다
→ 토큰이 인터넷에 공개됨 → 보안 사고!
올바른 예)
방법 1: .mcp.json을 .gitignore에 넣어서 Git이 무시하게 한다
방법 2: 토큰을 환경 변수로 따로 관리한다
→ 비밀 정보는 절대 Git에 올리지 않는다
이럴 때 이렇게:
PAT를 넣은 직후, 가장 먼저 .gitignore에 .mcp.json을 적어두세요.
까먹고 커밋하면 토큰을 새로 발급받아야 하는 번거로움이 생겨요.
잠깐 점검
.mcp.json의 구조(mcpServers > 서버명 > command/args/env)가 그려지나요?
PAT는 어디서 받고, Project Ref는 어디서 보나요?
Context7 MCP 등록 명령어가 기억나나요?
두 MCP의 역할 차이를 말할 수 있나요?
.mcp.json을 Git에 올리면 안 되는 이유를 아나요?
19-3 진짜 DB로 이사하기 (마이그레이션)
이런 적 있죠?
방을 정리하다가 "에라, 일단 다 옮기고 보자"며 짐을 마구 새 방에 던져 넣은 적 있죠? 나중에 보면 가구 위치는 엉망이고, 어떤 물건은 어디 갔는지 안 보여요.
DB로 데이터를 옮길 때도 똑같아요. 계획 없이 바로 옮기면 구조가 엉켜요.
그래서 "이사 계획서"를 먼저 만드는 게 마이그레이션이에요.
그게 마이그레이션이에요
마이그레이션(Migration)은 데이터를 옛 집에서 새 집으로 옮기는 이사예요. 가짜 데이터(임시 집)에서 진짜 DB(새 집)로 옮기는 거죠.
이사 계획서가 곧 마이그레이션 파일(SQL)이에요.
이사 계획서에 적는 것:
1. 새 집 구조 설계 → CREATE TABLE (테이블 만들기)
2. 가구 배치 → 컬럼 정의 (name, cost, likes 등)
3. 방 주인 정하기 → 외래키 (이 데이터는 이 사용자 거!)
4. 짐 옮기기 → 데이터 넣기
| 비유 | 클로드 코드에서 | 주의 |
|---|---|---|
| 이사 | 마이그레이션 (가짜 데이터 → 진짜 DB) | 되돌리기 어려움, 신중히 |
| 이사 계획서 | 마이그레이션 파일 (SQL) | 클로드 코드가 자동 작성 |
| 방 한 칸 | 테이블 (cities, user_preferences) | 표 한 장과 비슷 |
| 방 주인 표시 | 외래키 (user_id로 주인 연결) | 잘못 연결하면 데이터 꼬임 |
안전한 길: "계획 먼저, 구현 나중" 2단계
DB 작업은 한 번 잘못하면 되돌리기가 힘들어요. 그래서 한 번에 하지 않고 두 단계로 나눠요.
1단계는 "계획만 알려줘"예요.
프롬프트 예:
"context7과 Supabase MCP를 사용해서 현재 웹사이트를
Supabase 백엔드와 연동하려면 어떻게 해야 하는지
계획을 알려줘. 계획만 알려주고 코드는 아직 작성하지마."
이러면 클로드 코드가 프로젝트를 살펴보고, 어떤 테이블을 어떻게 만들지 그림만 보여줘요. 마음에 안 들면 여기서 고쳐달라고 하면 돼요. 아직 코드는 안 건드렸으니까요.
2단계는 "계획대로 구현해줘"예요.
프롬프트 예:
"계획대로 구현해줘"
이러면 그제야 마이그레이션 파일(SQL)을 만들고, Supabase에 테이블을 진짜로 만들고, 화면·서버 코드도 DB에 맞게 고쳐요.
왜 굳이 두 단계로 나눌까
한 번에 "구현해줘"라고 하면 편할 텐데 왜 나눌까요?
잘못된 예) 바로 구현 요청
"바로 구현해줘"
→ 테이블 구조가 잘못 설계돼도 그냥 만들어버림
→ 데이터까지 들어간 뒤 구조 고치기 = 매우 위험
올바른 예) 계획 → 확인 → 구현
"계획 먼저" → 테이블 설계를 눈으로 확인 → 마음에 들면 "구현"
→ 잘못된 부분은 코드 만들기 전에 미리 수정
→ 안전하고 체계적
이사도 계획서를 먼저 보고 "여기 가구 위치 바꿔주세요" 하면 편하잖아요. 짐 다 옮긴 뒤에 바꾸면 훨씬 힘들고요.
클로드 코드가 분석한 프로젝트 상태
1단계를 시키면 클로드 코드가 이런 식으로 현황을 정리해 보여줘요.
기존 설정:
- Supabase 프로젝트 연결됨
- Next.js 15.3.5 + App Router 사용
- 인증 미들웨어 구성됨
- 클라이언트/서버 Supabase 유틸리티 존재
지금 데이터 구조:
- 도시 20개 정적 데이터 (JSON) → cities 테이블로 이사
- 사용자 선호도 (localStorage 저장) → user_preferences 테이블로 이사
- 도시별 8개 점수 → jsonb 컬럼으로 저장
(버전 번호 같은 값은 원서/노트 기준 예시예요. 본인 프로젝트 상황은 다를 수 있어요.)
여기서 "정적 데이터"는 코드 안에 고정해둔 데이터예요. 일상 비유로는 "벽에 붙여둔 안내문" 같은 거예요. 바뀌지 않고 그대로 있죠.
클로드 코드가 자동 설계한 테이블
2단계로 가면 클로드 코드가 이런 테이블을 설계해서 만들어줘요. 먼저 도시 정보를 담는 cities 테이블이에요.
-- 1. cities 테이블 (도시 정보)
cities (
id integer PRIMARY KEY, -- 도시 고유 번호
name text NOT NULL, -- 도시 이름 (꼭 있어야 함)
tagline text, -- 한 줄 소개
cost text, -- 생활비
internet text, -- 인터넷 속도
likes integer DEFAULT 0, -- 좋아요 수 (기본값 0)
dislikes integer DEFAULT 0, -- 싫어요 수 (기본값 0)
image text, -- 도시 이미지 주소
badges text[], -- 배열: 뱃지 여러 개를 한 칸에
scores jsonb, -- JSON: 8개 점수를 유연하게
region text, -- 지역
budget text, -- 예산 범위
environment text, -- 환경 특성
best_season text, -- 최적 시즌
description text, -- 설명
created_at timestamp DEFAULT now() -- 생성 시각 (자동)
)
그다음 사용자 선호도를 담는 user_preferences 테이블이에요.
-- 2. user_preferences 테이블 (사용자 선호도)
public.user_preferences (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
-- 아래 한 줄을 Supabase MCP가 알아서 넣어줌 --
user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL
-- ... 선호도 컬럼들 ...
)
여기 어려운 부분만 한 줄씩 풀어볼게요.
PRIMARY KEY(기본키)는 "이 표에서 줄마다 붙는 학번"이에요. 겹치지 않는 고유 번호죠. NOT NULL은 "이 칸은 비워두면 안 됨"이에요. DEFAULT 0은 "안 적으면 그냥 0으로 채워줌"이에요.
방금 우리는 표(테이블) 하나를 통째로 설계했어요. 어떤 칸(컬럼)을 둘지, 그 칸에 어떤 종류(타입)의 값이 들어갈지를 쭉 정한 거죠.
용어 한 줄: 이렇게 테이블·컬럼·타입·관계로 짠 데이터 구조 설계를 스키마(Schema)라고 불러요. (DB의 "설계도" — 일상 비유로는 새 집을 짓기 전에 그리는 "건물 도면" 같은 거예요.)
도면 한 장에 방 개수, 방 크기, 어디에 문을 낼지가 다 적혀 있듯이, 스키마 한 벌에 테이블·컬럼·타입·관계가 다 담겨 있어요.
외래키와 ON DELETE CASCADE
user_preferences의 user_id 줄이 좀 어려워 보이죠. 하나씩 풀면 어렵지 않아요.
REFERENCES auth.users(id)는 외래키예요. 외래키는 "이 데이터의 주인이 누구인지 가리키는 손가락"이에요. auth.users는 Supabase가 기본으로 주는 "회원 명단" 표예요. 즉 "이 선호도는 회원 명단의 이 사람 거예요"라고 연결하는 거죠.
ON DELETE CASCADE는 "주인이 사라지면 그 짐도 같이 치워줘"라는 옵션이에요. 회원이 탈퇴하면, 그 사람의 선호도 데이터도 자동으로 같이 지워줘요.
이게 왜 중요한지 잘못된 예로 봐요.
잘못된 예) CASCADE 없이 두면
회원이 탈퇴함 → 회원 명단에선 사라짐
→ 그런데 그 사람의 선호도 데이터는 남아 있음
→ 주인 없는 "고아 데이터"가 계속 쌓임
→ 심하면 탈퇴 자체가 거부됨 (기본값 RESTRICT 때문)
올바른 예) CASCADE 있으면
회원이 탈퇴함 → 그 사람 선호도 데이터도 자동으로 같이 삭제
→ 쓰레기 데이터 안 남음 → 깔끔하게 정리됨
고아 데이터는 주인이 사라져 연결이 끊긴 쓸모없는 데이터예요. 일상 비유로는 "주인 없는 택배"가 창고에 계속 쌓이는 거예요.
Supabase MCP가 알아서 해주는 것
여기서 Supabase MCP의 진짜 가치가 드러나요. 저 auth.users 연결이나 마이그레이션 파일은 사실 직접 하려면 까다로워요.
MCP가 없었다면:
auth.users 참조 방법을 직접 찾아야 함
마이그레이션 파일 쓰는 법을 손으로 배워야 함
Supabase만의 특수 패턴을 공식 문서에서 일일이 확인
MCP가 있으면:
auth.users 참조 같은 Supabase 특유 패턴을 자동으로 넣어줌
마이그레이션 파일까지 전부 자동 생성
테이블이 안 보이면 "테이블 구조 보여줘"로 다시 확인 가능
한 걸음 더 ▸ 특수 데이터 타입 3종
위 SQL에 평소 안 보던 타입 몇 개가 나왔어요. PostgreSQL(Supabase가 쓰는 DB)이 주는 편한 타입들이에요. 지금 다 외울 필요는 없고, "이런 게 있구나" 정도면 충분해요.
| 타입 | 한 줄 뜻 | 예시 |
|---|---|---|
text[] |
여러 값을 한 칸에 (배열) | badges: ['해변도시', '저비용'] |
jsonb |
자유로운 키-값 묶음 (JSON) | scores: {"safety": 8, "wifi": 9} |
uuid |
안 겹치는 긴 고유 번호 | 550e8400-e29b-41d4... |
timestamp |
날짜+시간 | 2024-03-15 14:30:00 |
text[]는 "한 칸에 목록을 통째로" 넣고 싶을 때 써요. jsonb는 "점수 8개처럼 묶음 데이터"를 유연하게 담을 때 써요. uuid는 학번 대신 쓰는 "엄청 길어서 안 겹치는 번호표"예요.
DB 작업은 혼자 집중해서
마지막으로 작업 순서 팁이 하나 있어요.
잘못된 예) 동시에 여러 작업
터미널 1: DB 마이그레이션 작업 중
터미널 2: 동시에 UI 기능 개발 중
→ DB 구조가 바뀌는 도중에 UI 코드와 충돌!
올바른 예) DB 작업은 단독으로
DB 마이그레이션만 집중해서 끝낸다
→ 그다음에 다른 작업 시작
저자도 "데이터베이스 관련 작업은 중요한 작업이라 다른 작업과 병렬로 처리하지 않는 걸 추천"한다고 했어요.
이사하는 중에 다른 공사까지 같이 하면 사고 나기 쉽잖아요. DB 이사도 그래요. 하나씩 차분히 하는 게 안전해요.
잠깐 점검
마이그레이션을 "계획 → 구현" 2단계로 나누는 이유를 아나요? cities 테이블과 user_preferences 테이블의 역할 차이를 아나요? user_id가 auth.users(id)를 참조하는 이유를 설명할 수 있나요? ON DELETE CASCADE가 없으면 무슨 문제가 생기는지 아나요? text[], jsonb, uuid의 쓰임을 구분할 수 있나요? DB 작업을 다른 작업과 동시에 하면 안 되는 이유를 아나요?
직접 해보기
연습 1: .mcp.json 빈칸 채우기 (기본)
아래 빈칸 A, B, C를 채워보세요.
{
"mcpServers": {
"supabase": {
"command": "___A___",
"args": [
"-y",
"___B___",
"--read-only",
"--project-ref=abc123"
],
"env": {
"___C___": "sbp_xxxx..."
}
}
}
}
정답:
A는 npx (Node 패키지 실행 도구)예요.
B는 @supabase/mcp-server-supabase@latest (Supabase MCP 패키지)예요.
C는 SUPABASE_ACCESS_TOKEN (PAT를 넣는 환경 변수 이름)이에요.
연습 2: 2단계 프롬프트 만들기 (중급)
내 프로젝트에 products(상품)와 orders(주문) 테이블이 필요해요. 2단계 전략으로 클로드 코드에게 줄 프롬프트를 써보세요.
정답 예시:
1단계 (계획): "context7과 Supabase MCP를 사용해서 products 테이블과 orders 테이블을 설계해줘. orders는 products를 참조해야 하고, 사용자 인증도 연동해야 해. 계획만 알려주고 코드는 아직 작성하지마."
2단계 (구현): "계획대로 구현해줘"
핵심은 "계획만, 코드는 아직"을 1단계에서 분명히 말하는 거예요. 그리고 DB 작업이니 다른 기능 개발과 동시에 돌리지 않아요.
연습 3: CASCADE 빼면? (심화)
아래 SQL에서 ON DELETE CASCADE를 빼면 무슨 일이 생길까요?
CREATE TABLE public.user_preferences (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL
);
정답: CASCADE를 빼면 기본값은 RESTRICT(거부)로 동작해요.
그러면 이런 문제가 생겨요. 회원이 탈퇴하려는데 선호도 데이터가 남아 있으면, 탈퇴(삭제) 자체가 거부돼요. 설령 지워지더라도 주인 없는 고아 데이터가 남아요. 시간이 지날수록 쓰레기 데이터가 쌓여요.
CASCADE를 넣으면 주인이 지워질 때 짐도 같이 지워져서 깔끔해져요.
연습 4: 한 개만 깔면? (중급)
Supabase MCP만 깔고 Context7 MCP를 안 깔면 뭐가 아쉬울까요? 반대로 Context7만 있고 Supabase MCP가 없으면요?
정답: Supabase MCP만 있으면, DB는 직접 다루지만 최신 사용법을 몰라 옛 방식으로 에러가 날 수 있어요. Context7만 있으면, 최신 문서는 보지만 DB를 직접 못 건드려서 실행은 손으로 해야 해요. 그래서 둘 다 깔아야 "최신 정보 + 직접 실행"이 완성돼요.
이 장 한눈에 정리
DB는 앱의 메모장이에요. 있어야 데이터가 안 사라져요. 가짜 데이터로 먼저 만들고, 잘 돌면 진짜 DB로 갈아타요. DB를 다루려면 통역사 둘(Supabase MCP, Context7 MCP)을 끼워요. Supabase MCP는 직접 작성(PAT 필요), Context7 MCP는 명령어 한 줄(인증 불필요)이에요. PAT 같은 비밀값은 절대 Git에 올리지 않아요. DB 이사는 "계획 먼저, 구현 나중" 2단계로 안전하게 해요. 외래키와 ON DELETE CASCADE로 데이터를 깔끔하게 연결·정리해요.
다음 장 예고: 이렇게 연결한 DB 위에서 데이터를 더 다뤄볼 거예요. 지금 몰라도 됩니다.
클릭하거나 Space를 눌러 뒤집기